//
// Created by SDSC\202100300261 on 23-11-13.
//

#include "BLOCK.h"
#include "MyException.h"

BLOCK::BLOCK() {
    this->table = new TABLE();
}

BLOCK::BLOCK(std::vector<TOKEN *>* _tokens,CodeTable * _codeTable) {
    index = 0;
    this->codeTable = _codeTable;
    this->table = new TABLE();
    tokens = _tokens;
}

TOKEN *BLOCK::getNowToken() {
    if(index >= (tokens -> size()) - 1) {
        return nullptr;
    }
//    std::cout<<nameSym((*tokens)[0] -> sym);
//std::cout<<tokens -> size()<<std::endl;
    return (*tokens)[index];
}

TOKEN *BLOCK::getToken() {
    if(index > (tokens -> size()) - 1) {
        return nullptr;
    }
    return (*tokens)[index++];
}

bool BLOCK::checkSym(TOKEN *token, SYMS sym) {
    if(token == nullptr) {
        throw MyException("unexpect token NILL;  need " + nameSym(sym));
    }
    if(!softCheck(token, sym)) {
        throw MyException("unexpect token " + nameSym(token ->sym) + ";  need " + nameSym(sym));
    }
    return true;
}
bool BLOCK::softCheck(TOKEN *token, SYMS sym)  {
    if(token == nullptr || token -> sym != sym) {
        return false;
    }
    return true;
}

void BLOCK::loadNum(const std::string &s) {
    TABLE_ENTRY * entry = table -> getEntry(s);
    if(entry == nullptr) {
        throw MyException("undefined name " + s);
    }
    if(entry -> kind == SYMS::CONST) {
        codeTable->addCode(OPTS::LIT, 0, entry->value);
    }
    else if(entry -> kind == SYMS::VAR) {
        codeTable->addCode(OPTS::LOD, table->LEVEL - entry->level, entry->adr);
    }
    else {
        throw MyException("undefined name " + s);
    }
}
/**
 * 〈程序〉→〈分程序>.
 */
void BLOCK::execMain() {
    execSumPro();
    TOKEN *token = getToken();
    checkSym(token, SYMS::DOT);
//    codeTable ->addCode(OPTS::OPR,0,0);
//    std::cout<<*codeTable;
//    std::cout << "successfully compiler" << std::endl;

}

/**
 * 〈分程序〉→ [<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句>
 */
int BLOCK::execSumPro() {//返回入口地址


    while(softCheck(getNowToken(),SYMS::CONST)) {
        execConst();
    }
//    token = getNowToken();
    while(softCheck(getNowToken(),SYMS::VAR)) {
        execVar();
    }
    int index = codeTable -> addCode(OPTS::JMP,0,0);
//    token = getNowToken();
    while(softCheck(getNowToken(),SYMS::PROCEDURE)) {
        execPro();
    }

    codeTable -> addCode(OPTS::INT,0,table -> DX.top());//为过程分配空间，大小为数据单元数+3
    int now = codeTable -> getIndex();

    codeTable -> setCode(index,OPTS::JMP,0,now);//回填此程序的入口地址

    execSentence();

    return index;
}
/**
 *  <常量说明部分> → CONST<常量定义>{ ,<常量定义>}；
 * <常量定义> → <标识符>=<无符号整数>
 */
void BLOCK::execConst() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::CONST);
    do {

        TOKEN *token1 = getToken();
        checkSym(token1,SYMS::IDENT);
        token = getToken();
        checkSym(token,SYMS::EQUAL);
        TOKEN *token2 = getToken();
        checkSym(token2,SYMS::NUMBER);
        table -> addConst(token1 -> id,token2 -> num);
        token = getToken();
    }while(softCheck(token,SYMS::COMMA));
    checkSym(token,SYMS::SEMICOLON);
//    std::cout<<*table;
}

/**
 *  <变量说明部分> → VAR<标识符>{ ,<标识符>}；
 */
void BLOCK::execVar() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::VAR);
    do {
        token = getToken();
        checkSym(token,SYMS::IDENT);
        table -> addVar(token -> id);
        token = getToken();
    }while(softCheck(token,SYMS::COMMA));

    checkSym(token,SYMS::SEMICOLON);

//    std::cout<<*table;
}

/**
* <过程说明部分> → <过程首部><分程序>;{<过程说明部分>}
 * <过程首部> → procedure<标识符>；
*/
void BLOCK::execPro() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::PROCEDURE);
    token = getToken();
    checkSym(token,SYMS::IDENT);
    int ind = table ->addPro(token -> id);
    token = getToken();
    checkSym(token,SYMS::SEMICOLON);
    int adr = execSumPro();
    table ->setPro(ind,adr);//设置过程的入口地址
//    std::cout<<*codeTable<<"\n\n\n";
    token = getToken();
    checkSym(token,SYMS::SEMICOLON);
    codeTable ->addCode(OPTS::OPR,0,0);//过程结束，返回调用点
    table -> killPro();
}
/**
*  <语句> → <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空>
*/
void BLOCK::execSentence() {
        TOKEN *token = getNowToken();
        if(token == nullptr) {
            return;
        }
        if(softCheck(getNowToken(),SYMS::IDENT)) {

            execAssign();
        }
        if(softCheck(getNowToken(),SYMS::IF)) {
            execIf();
        }
        if(softCheck(getNowToken(),SYMS::WHILE)) {
            execWhile();
        }
        if(softCheck(getNowToken(),SYMS::CALL)) {
            execCall();
        }
        if(softCheck(getNowToken(),SYMS::READ)) {
            execRead();
        }
        if(softCheck(getNowToken(),SYMS::WRITE)) {
            execWrite();
        }
        if(softCheck(getNowToken(),SYMS::BEGIN)) {
            execBegin();
        }
}
/**
 *  <赋值语句> → <标识符>:=<表达式>
 */
void BLOCK::execAssign() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::IDENT);
    TABLE_ENTRY *entry = table -> getEntry(token -> id);
//    std::cout<<entry<<std::endl;
    if(entry == nullptr) {
        throw MyException("undefined name " + token -> id);
    }
    token = getToken();
    checkSym(token,SYMS::VAR_ASSIGN);
    execExpr();//先执行表达式
    /**
     * 执行完之后，栈顶是表达式的值，entry -> adr是变量的地址
     * 赋值的变量应当是比自己更低层的变量
     */
    codeTable ->addCode(OPTS::STO, table-> LEVEL - entry -> level,entry -> adr);
//    std::cout<<*codeTable;
//    std::cout<<"!!!!!!!!!";

    delete entry;//动态申请的内存需要释放，防止内存泄漏
}


/**
 * <条件语句> → if<条件>then<语句>[else<语句>]
 * 条件执行语句，当条件为真时，opr结果为0，那么顺序执行，否则需要跳转到else语句
 * 跳转需要知道then语句块结束后的地址，所以这里需要返填
 */
void BLOCK::execIf() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::IF);
    execCondition();

    int ind = codeTable ->addCode(OPTS::JPC,0,0);//需要一个跳转指令，但是此时不知道跳转地址，所以先填0，完成之后需要回填

    token = getToken();
    checkSym(token,SYMS::THEN);
    execSentence();
    token = getNowToken();
    int adr = codeTable -> getIndex();
    codeTable -> setCode(ind,OPTS::JPC,0,adr + 1);//回填跳转地址
    if(softCheck(token,SYMS::ELSE)) {//如果包含else 语句
        getToken();//token指针加1
        execSentence();
    }
}

/**
 * <当型循环语句> → while<条件>do<语句>
 */

void BLOCK::execWhile() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::WHILE);
    int whileAdr = (codeTable -> getIndex()) + 1;//记录while语句的入口地址
    execCondition();
    int ind = codeTable ->addCode(OPTS::JPC,0,0);//需要返填跳转地址
    token = getToken();
    checkSym(token,SYMS::DO);
    execSentence();
    codeTable -> addCode(OPTS::JMP,0,whileAdr);//跳转到while语句的入口地址
//    std::cout<<"!!!"<<whileAdr<<std::endl;
    int adr = codeTable -> getIndex();
    codeTable -> setCode(ind,OPTS::JPC,0,adr + 1);//回填跳转地址
}

/**
 *  <读语句> → read(<标识符>{ ，<标识符>})
 */
void BLOCK::execRead() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::READ);
    token = getToken();
    checkSym(token,SYMS::LEFT_BRACKET);
    do {
        token = getToken();
        checkSym(token,SYMS::IDENT);
        TABLE_ENTRY *entry = table -> getEntry(token -> id);
        if(entry == nullptr) {
            throw MyException("undefined name " + token -> id);
        }
        if(entry -> kind != SYMS::VAR) {
            throw MyException("can't read a non-var");
        }
        codeTable -> addCode(OPTS::OPR,0,16);//定义16为读操作
        codeTable -> addCode(OPTS::STO,table -> LEVEL - entry -> level,entry -> adr);//将数保存起来
        token = getToken();
    } while(softCheck(token,SYMS::COMMA));
    checkSym(token,SYMS::RIGHT_BRACKET);
}

/**
 *  <写语句> → write(<标识符>{，<标识符>})
 */
void BLOCK::execWrite() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::WRITE);
    token = getToken();
    checkSym(token,SYMS::LEFT_BRACKET);
    do {
        token = getToken();
        checkSym(token,SYMS::IDENT);
        TABLE_ENTRY *entry = table -> getEntry(token -> id);
        if(entry == nullptr) {
            throw MyException("undefined name " + token -> id);
        }
        loadNum(token -> id);// 将数加载到栈顶
        codeTable -> addCode(OPTS::OPR,0,14);//定义14为写操作
        token = getToken();
    } while(softCheck(token,SYMS::COMMA));
    checkSym(token,SYMS::RIGHT_BRACKET);
    codeTable -> addCode(OPTS::OPR,0,15);//定义15为换行操作
}

/**
 *  <复合语句> → begin<语句>{ ；<语句>}<end>
 */

void BLOCK::execBegin() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::BEGIN);
    do {
        execSentence();
        token = getToken();
    }while(softCheck(token,SYMS::SEMICOLON));
    checkSym(token,SYMS::END);
//    std::cout<<*codeTable;
}

/**
 *  <过程调用语句> → call<标识符>
*/
void BLOCK::execCall() {
    TOKEN *token = getToken();
    checkSym(token,SYMS::CALL);
    token = getToken();
    checkSym(token,SYMS::IDENT);
    TABLE_ENTRY *entry = table -> getEntry(token -> id);
    if(entry == nullptr) {
        throw MyException("undefined name " + token -> id);
    }
    //调用过程应当是调用比自己更低层或同一层次的过程
    codeTable -> addCode(OPTS::CAL,table->LEVEL  - entry -> level,entry -> adr);
    delete entry;
}
/**
 *  <条件> → <表达式><关系运算符><表达式>|ood<表达式>
 *   <关系运算符> → =|#|<|<=|>|>=
 *   所有的OPR都定义为次栈顶为第一运算符
 */
void BLOCK::execCondition() {
    TOKEN *token = getNowToken();
    if(softCheck(token,SYMS::ODD)) {
        getToken();
        execExpr();
        codeTable -> addCode(OPTS::OPR,0,6);
    }else {
        execExpr();
        token = getToken();
        execExpr();
        switch (token -> sym) {
            case SYMS::EQUAL:
                codeTable -> addCode(OPTS::OPR,0,8);
                break;
            case SYMS::NOT_EQUAL:
                codeTable -> addCode(OPTS::OPR,0,9);
                break;
            case SYMS::LESS:
                codeTable -> addCode(OPTS::OPR,0,10);
                break;
            case SYMS::RIGHT_EQUAL:
                codeTable -> addCode(OPTS::OPR,0,11);
                break;
            case SYMS::GREATER:
                codeTable -> addCode(OPTS::OPR,0,12);
                break;
            case SYMS::LESS_EQUAL:
                codeTable -> addCode(OPTS::OPR,0,13);
                break;
            default:
                break;
        }
    }
}
/**
 * <表达式> → [+|-]<项>{<加减运算符><项>}
 */
void BLOCK::execExpr() {
    TOKEN *token = getNowToken();
    if(token -> sym == SYMS::PLUS || token -> sym == SYMS::MINUS) {
//        std::cout<<"!!!"<<std::endl;
        token = getToken();
        execItem();
        if (token->sym == SYMS::MINUS) {
            codeTable->addCode(OPTS::OPR, 0, 1);
        }
    }
    else {
        execItem();
    }
    while(softCheck(getNowToken(),SYMS::PLUS) || softCheck(getNowToken(),SYMS::MINUS)) {
        token = getToken();
        execItem();
        if (token->sym == SYMS::PLUS) {
            codeTable->addCode(OPTS::OPR, 0, 2);
        } else {
            codeTable->addCode(OPTS::OPR, 0, 3);
        }
    }
}
/**
*  <项> → <因子>{<乘除运算符><因子>}
*/

void BLOCK::execItem() {
    execFactor();
    while(softCheck(getNowToken(),SYMS::MULTIPLY) || softCheck(getNowToken(),SYMS::DIVIDE)) {
        TOKEN *token = getToken();
        execFactor();
        if (token->sym == SYMS::MULTIPLY) {
            codeTable->addCode(OPTS::OPR, 0, 4);
        } else {
            codeTable->addCode(OPTS::OPR, 0, 5);
        }
    }
}
/**
*  <因子> → <标识符>|<无符号整数>|(<表达式>)
*/
void BLOCK::execFactor() {
    TOKEN *token = getToken();
    if(token -> sym == SYMS::IDENT) {
        TABLE_ENTRY *entry = table -> getEntry(token -> id);
        if(entry == nullptr) {
            throw MyException("undefined name " + token -> id);
        }
        loadNum(token -> id);
    }
    else if(token -> sym == SYMS::NUMBER) {
        codeTable -> addCode(OPTS::LIT,0,token -> num);
    }
    else if(token -> sym == SYMS::LEFT_BRACKET) {
        execExpr();
        token = getToken();
        checkSym(token,SYMS::RIGHT_BRACKET);
    }
    else {
        throw MyException("unexpect token " + nameSym(token ->sym) + ";  need IDENT or NUMBER or LEFT_BRACKET");
    }
}
